﻿/* texturefill.cpp
 * 
 * Copyright (C) 2022 Yuan LI
 * email:18069211@qq.com
 * https://blog.csdn.net/liyuanbhu
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "texturefill.h"
#include <QDebug>
#include <QStack>

TextureFill::TextureFill()
{

}

inline static QRgb ** linePointer(QImage &image)
{
    int rows = image.height();
    QRgb **lines = new QRgb * [rows];
    for(int row = 0; row < rows; row++)
    {
        lines[row] = (QRgb *)image.scanLine(row);
    }
    return lines;
}

inline static void computeRoundPoints(QPoint roundPoint[8], QPoint center, int type) //type == 4 或 8
{
    roundPoint[0] = center + QPoint(0, -1);  // 4  0  5
    roundPoint[1] = center + QPoint(-1, 0);  // 1  x  2
    roundPoint[2] = center + QPoint(1, 0);   // 6  3  7
    roundPoint[3] = center + QPoint(0, 1);
    if(type == 8)
    {
        roundPoint[4] = center + QPoint(-1, -1);
        roundPoint[5] = center + QPoint(1, -1);
        roundPoint[6] = center + QPoint(-1, 1);
        roundPoint[7] = center + QPoint(1, 1);
    }
}

bool TextureFill::fillTexture(QImage &image, QPoint seedPoint, QImage & texture, int type) //type == 4 或 8
{
    if(image.isNull() || texture.isNull() || image.depth() != 32 || texture.depth() != 32)
    {
        return false;
    }
    QRect rect = image.rect();
    if(!rect.contains(seedPoint))
    {
        qDebug() << "Seed position is out of range!";
        qDebug() << "----image.size() is " << image.size();
        qDebug() << "----seedPoint is " << seedPoint;
        return false;
    }
    QRgb seedColor = image.pixel(seedPoint);
    QRgb fillColor = qRgb(255, 0, 0);
    if(qRed(seedColor) > 128)
    {
        fillColor = qRgb(0, 0, 0); //只要保证 fillColor 和 seedColor 不相同就行。这个颜色就是随便选的，也可以选别的颜色。
    }

    QImage imageClone = image.copy();//在这个图像上填充单一颜色，原始图像上填充 Texture
    QRgb **orgImgLines = linePointer(image);
    QRgb **tempImgLines = linePointer(imageClone);
    QRgb **textureLines = linePointer(texture);

    int textureWidth = texture.width();
    int textureHeight = texture.height();

    QStack<QPoint> stack;
    stack.push(seedPoint);
    while(!stack.isEmpty())
    {
        QPoint p = stack.pop();
        int row = p.y();
        int col = p.x();
        tempImgLines[row][col] = fillColor;
        orgImgLines[row][col] = textureLines[row % textureHeight][col % textureWidth];

        QPoint roundPoint[8];
        computeRoundPoints(roundPoint, p, type);

        for(int i = 0; i < type; i++)
        {
            row = roundPoint[i].y();
            col = roundPoint[i].x();
            if(rect.contains(roundPoint[i]) && tempImgLines[row][col] == seedColor)
            {
                tempImgLines[row][col] = fillColor;//重要：提前填充可以避免这个像素被多次压栈到 stack。速度提升至少三倍。
                orgImgLines[row][col] = textureLines[row % textureHeight][col % textureWidth];
                stack.push(roundPoint[i]);
            }
        }
    }
    delete[] orgImgLines;
    delete[] tempImgLines;
    delete[] textureLines;
    return true;
}

bool TextureFill::fillColor(QImage &image, QPoint seedPoint, QRgb fillColor, int type)
{
    if(image.isNull() || image.depth() != 32)
    {
        return false;
    }
    QRect rect = image.rect();
    if(!rect.contains(seedPoint))
    {
        qDebug() << "Seed position is out of range!";
        qDebug() << "----image.size() is " << image.size();
        qDebug() << "----seedPoint is " << seedPoint;
        return false;
    }
    QRgb seedColor = image.pixel(seedPoint);
    if(seedColor == fillColor)
    {
        return true;// 如果颜色相同就不需要填充，而且也不能填充，否则会导致死循环。
    }
    QStack<QPoint> stack;
    stack.push(seedPoint);

    QRgb **lines = linePointer(image);
    while(!stack.isEmpty())
    {
        QPoint p = stack.pop();
        int row = p.y();
        int col = p.x();
        lines[row][col] = fillColor;

        QPoint roundPoint[8];
        computeRoundPoints(roundPoint, p, type);

        for(int i = 0; i < type; i++)
        {
            row = roundPoint[i].y();
            col = roundPoint[i].x();
            if(rect.contains(roundPoint[i]) && lines[row][col] == seedColor)
            {
                lines[row][col] = fillColor;//重要：提前填充可以避免这个像素被多次压栈到 stack。速度提升至少三倍。
                stack.push(roundPoint[i]);
            }
        }
    }
    delete[] lines;
    return true;
}
